package pers.vic.upload.socket.service;

import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import java.io.File;
import java.io.IOException;
import java.nio.ByteBuffer;
import java.nio.channels.FileChannel;
import java.nio.file.StandardOpenOption;
import java.util.Iterator;
import java.util.Map;
import java.util.concurrent.ConcurrentHashMap;
import java.util.concurrent.ScheduledExecutorService;
import java.util.concurrent.ScheduledThreadPoolExecutor;
import java.util.concurrent.TimeUnit;

/**
 * 描述:文件通道持有
 * @author Vic.xu
 * @date 2022-07-18 10:22
 */
public class FileChannelHolder {
    private static final Logger LOGGER = LoggerFactory.getLogger(FileChannelHolder.class);
    private static final Map<File, FileChannelHolder> HOLDER_MAP = new ConcurrentHashMap<>();
    //文件关闭延迟毫秒数
    private static final long IDLE_MILLIS = 1000L;

    /**
     * 固定延迟定时任务
     */
    private static ScheduledExecutorService scheduledExecutorService;

    /**
     * 文件channel
     */
    private final FileChannel channel;
    /**
     * channel 是否在忙（正在写入）
     */
    private volatile boolean busy;

    /**
     * channel可关闭时间
     */
    private volatile long closeTime;

    /**
     * 创建一个FileChannelHolder  并打开FileChannel 准备写入
     * @param file File
     * @return FileChannelHolder
     */
    public static FileChannelHolder get(File file){
        if (scheduledExecutorService == null) {
            createScheduled();
        }
        return HOLDER_MAP.computeIfAbsent(file, f->{
            try {
                return new FileChannelHolder(f);
            } catch (IOException e) {
                throw new AssertionError(e);
            }
        });
    }

    /**
     * 删除一个FileChannelHolde
     * @param file file
     * @return FileChannelHolder
     */
    public static FileChannelHolder remove(File file){
        return HOLDER_MAP.remove(file);
    }

    /**
     * 创建定时器:定时关闭文件
     */
    private static synchronized void createScheduled() {
        if (scheduledExecutorService != null) {
            return;
        }
        scheduledExecutorService = new ScheduledThreadPoolExecutor(1);
        scheduledExecutorService.schedule(()->{
            if (HOLDER_MAP.isEmpty()) {
                return;
            }
            Iterator<Map.Entry<File, FileChannelHolder>> iterator = HOLDER_MAP.entrySet().iterator();
            long now = System.currentTimeMillis();
            while (iterator.hasNext()) {
                Map.Entry<File, FileChannelHolder> next = iterator.next();
                FileChannelHolder holder = next.getValue();
                boolean canClose = !holder.busy && now > holder.closeTime;
                if (canClose) {
                    iterator.remove();
                    try {
                        LOGGER.debug("close file:{}" , next.getKey());
                        holder.channel.close();
                    } catch (IOException e) {
                        LOGGER.error("close file error", e);
                    }
                }
            }
        }, IDLE_MILLIS, TimeUnit.MILLISECONDS);
    }


    private FileChannelHolder(File file) throws IOException {
        LOGGER.debug("open file: {}", file);
        channel = FileChannel.open(file.toPath(), StandardOpenOption.CREATE, StandardOpenOption.WRITE);
    }

    /**
     * 写入文件
     * @param writePosition 写入位置
     * @param buffer 写入的内容
     */
    public void write(long writePosition, ByteBuffer buffer) throws IOException {
        busy = true;
        if (channel.position() != writePosition) {
            channel.position(writePosition);
        }
        channel.write(buffer);
        busy = false;
        closeTime = System.currentTimeMillis() + IDLE_MILLIS;
    }
}
